package star.cloud.asme.flowable.processInstance;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.flowable.variable.api.persistence.entity.VariableInstance;
import org.springframework.stereotype.Service;
import star.cloud.asme.base.dto.Result;
import star.cloud.asme.base.fowable.VO.ProcessInstanceQueryCondition;
import star.cloud.asme.base.fowable.VO.ProcessInstanceVO;
import star.cloud.asme.base.fowable.baseInterface.FlowableVOGeneratorService;
import star.cloud.asme.base.utils.ObjectsUtil;
import star.cloud.asme.flowable.processInstance.DTO.*;

import java.util.*;

@Service
@RequiredArgsConstructor
@Slf4j
public class HqProcessInstanceServiceImpl implements HqProcessInstanceService {

    private final RuntimeService runtimeService;
    private final IdentityService identityService;
    private final HistoryService historyService;
    private final TaskService taskService;
    private final RepositoryService repositoryService;
    private final FlowableVOGeneratorService voGeneratorService;

    @Override
    public Result<ProcessInstanceVO> getOneByCondition(String key, Object value) {
        HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery();
        switch (key) {
            case "processInstanceId" -> query.processInstanceId((String) value);
            case "deploymentId" -> query.deploymentId((String) value);
        }
        HistoricProcessInstance processInstance = query.singleResult();
        if (processInstance == null) {
            return Result.error();
        } else {
            return Result.ok(voGeneratorService.generateProcessInstanceVO(processInstance.getId()));
        }
    }

    @Override
    public Result<ProcessInstanceVO> getOneByConditions(ProcessInstanceQueryCondition conditions) {
        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(conditions.getProcessInstanceId()).singleResult();
        return Result.ok(voGeneratorService.generateProcessInstanceVO(processInstance.getId()));
    }

    @Override
    public Result<IPage<ProcessInstanceVO>> getListByConditionsWithPage(ProcessInstanceQueryCondition conditions) {
        IPage<ProcessInstanceVO> iPage = new Page<>(conditions.getCurrentPage(), conditions.getPageSize());
        HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery();

        if (ObjectsUtil.noNullAndEmpty(conditions.getProcessInstanceName())) {
            processInstanceQuery.processInstanceNameLike("%" + conditions.getProcessInstanceName() + "%");
        }

        if (ObjectsUtil.noNullAndEmpty(conditions.getProcessInstanceStartUserId())) {
            processInstanceQuery.startedBy(conditions.getProcessInstanceStartUserId());
        }

        if (ObjectsUtil.noNullAndEmpty(conditions.getProcessInstanceInvolvedUser())) {
            processInstanceQuery.involvedUser(conditions.getProcessInstanceInvolvedUser());
        }

        if (ObjectsUtil.noNullAndEmpty(conditions.getProcessInstanceTaskAssignee())) {
            List<Task> taskList = taskService.createTaskQuery().taskAssignee(conditions.getProcessInstanceTaskAssignee()).list();
            List<String> processInstanceIds = taskList.stream().map(TaskInfo::getProcessInstanceId).toList();

            if (processInstanceIds.isEmpty()) {
                iPage.setTotal(0);
                iPage.setRecords(null);
                return Result.ok(iPage);
            } else {
                processInstanceQuery.processInstanceIds(new HashSet<>(processInstanceIds));
            }
        }

        if (ObjectsUtil.noNullAndEmpty(conditions.getProcessInstanceCompleted())) {
            if (conditions.getProcessInstanceCompleted()) {
                processInstanceQuery.finished();
            } else {
                processInstanceQuery.unfinished();
            }
        }

//        processInstanceQuery.notDeleted();

        iPage.setTotal(processInstanceQuery.count());
        List<ProcessInstanceVO> processInstanceVOList = new ArrayList<>();
        List<HistoricProcessInstance> instanceList = processInstanceQuery.orderByProcessInstanceStartTime().desc().listPage(voGeneratorService.firstResult(iPage), voGeneratorService.maxResults(iPage));
        instanceList.forEach(item -> processInstanceVOList.add(voGeneratorService.generateProcessInstanceVO(item.getId())));
        iPage.setRecords(processInstanceVOList);
        return Result.ok(iPage);
    }


    @Override
    public Result<ProcessInstance> startProcessInstanceById(StartProcessInstanceParams startProcessInstanceParams) {
        ProcessDefinition processDefinition = getProcessDefinitionByKey(startProcessInstanceParams.getProcessDefinitionKey());
        identityService.setAuthenticatedUserId(startProcessInstanceParams.getLoginUsername());
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(), startProcessInstanceParams.getBusinessKey(), startProcessInstanceParams.getVariables());
        runtimeService.setProcessInstanceName(processInstance.getProcessInstanceId(), startProcessInstanceParams.getProcessInstanceName());
        return Result.ok(runtimeService.createProcessInstanceQuery().processInstanceId(processInstance.getId()).singleResult());
    }


    @Override
    public Result<?> logicalDeleteById(String id) {
        historyService.deleteHistoricProcessInstance(id);
        return Result.ok();
    }

    @Override
    public Result<?> physicalDeleteById(String id) {
        historyService.deleteHistoricProcessInstance(id);
        return Result.ok();
    }

    //废弃运行中的流程实例
    @Override
    public Result<?> deleteRuntimeProcessInstance(String processInstanceId, String deleteReason) {
        Map<String, VariableInstance> variableInstances = runtimeService.getVariableInstances(processInstanceId);
        variableInstances.forEach((variableName, variableInstance) -> runtimeService.removeVariable(processInstanceId, variableName));
        runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
        return Result.ok();
    }

    private ProcessDefinition getProcessDefinitionByKey(String processDefinitionKey) {
        return repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey).latestVersion().singleResult();
    }


}
